window.WebAssembly = WXWebAssembly;

import { NewID } from "./Utils/Util";

window.self = window;

export class ProgressEvent {}

export class TextDecoder {
  decode(bytes: number[]) {
    return decodeURIComponent(escape(String.fromCharCode(...bytes)));
  }
}

export class Worker {
  work;
  constructor(url) {
    this.work = (wx as any).createWorker(url);
    this.work.addEventListener = this.addEventListener;

    return this.work;
  }
  addEventListener(this: any, event: string, func: Function) {
    switch (event) {
      case "message":
        this.onMessage(func);
        break;
    }
  }
}

export class URL {
  static createObjectURL(blob: Blob) {
    let file = wx.getFileSystemManager();
    let path = `${(wx as any).env.USER_DATA_PATH}/${NewID()}`;
    if (blob.text) {
      file.writeFileSync(path, blob.text, "utf8");
    } else {
      file.writeFileSync(path, blob.bytes.buffer, "binary");
    }
    return path;
  }
}

export class Blob {
  config;
  bytes: Uint8Array;

  text: string = "";
  constructor(_bytesArr: ArrayBuffer[], _config) {
    if (_bytesArr.length) {
      if (typeof _bytesArr[0] == "string") {
        for (let i = 0; i < _bytesArr.length; i++) {
          this.text += _bytesArr[i];
        }
      } else {
        let len = 0;
        for (let i = 0; i < _bytesArr.length; i++) {
          len += _bytesArr[i].byteLength;
        }

        let bytes = new Uint8Array(len),
          offset = 0;
        for (let i = 0; i < _bytesArr.length; i++) {
          bytes.set(new Uint8Array(_bytesArr[i]), offset);
          offset += _bytesArr[i].byteLength;
        }

        this.bytes = bytes;
      }
    }
    this.config = _config;
  }
}

export class Request {
  url: string;
  config: any;
  constructor(_url, _config) {
    this.url = _url;
    this.config = _config;
  }
}

export class Response {
  stream: ReadableStream;
  constructor(_stream: ReadableStream) {
    this.stream = _stream;
  }

  arrayBuffer() {
    return this.stream.bytes;
  }

  text() {
    return this.stream.text;
  }
}

export class Headers {
  map: { [x: string]: string } = {};
  constructor(_config) {
    for (const key in _config) {
      this.map[key] = _config[key];
    }
  }

  get(key) {
    return this.map[key];
  }
}

export class ReadableStream {
  bytes: ArrayBuffer = new ArrayBuffer(0);
  text: string = "";
  controller = {
    enqueue: (value: ArrayBuffer | string) => {
      if (typeof value == "string") {
        this.text += value;
      } else {
        let mergedBytes = new Uint8Array(this.bytes.byteLength + value.byteLength);
        mergedBytes.set(new Uint8Array(this.bytes), 0);
        mergedBytes.set(new Uint8Array(value), this.bytes.byteLength);

        this.bytes = mergedBytes;
      }
    },
    close: () => {},
  };

  constructor(_activity) {
    _activity.start(this.controller);
  }
}

(window as any).Request = Request;
(window as any).Response = Response;
(window as any).Headers = Headers;
(window as any).ReadableStream = ReadableStream;
(window as any).Blob = Blob;
(window as any).URL = URL;
(window as any).Worker = Worker;
(window as any).ProgressEvent = ProgressEvent;
(window as any).TextDecoder = TextDecoder;

(window as any).fetch = function (req: Request) {
  return new Promise((res, rej) => {
    if (req.url.substring(0, 4) == "http") {
      wx.request({ url: req.url, success: res, fail: rej });
    } else {
      let fs = wx.getFileSystemManager();

      let encoding;

      if (req.url.indexOf("basis_transcoder") >= 0) {
        let flag = req.url.indexOf(".") + 1;
        if (req.url.substring(flag, req.url.length) == "js") {
          req.url = req.url.slice(0, flag) + "txt";
          encoding = "utf8";
        }
      }

      let content = encoding ? fs.readFileSync(req.url, encoding) : fs.readFileSync(req.url);
      let len = typeof content == "string" ? content.length : content.byteLength;

      res({
        status: 200,
        headers: new Headers({ "Content-Length": len }),
        body: {
          getReader: () => {
            return {
              content,
              n: 0,
              read: function () {
                return new Promise((resolve, _reject) => {
                  resolve({ done: this.n++ > 0 ? true : false, value: content });
                });
              },
            };
          },
          text: () => {
            return content;
          },
        },
      });
    }
  });
};
